﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Media3D;
using System.Windows.Input;
using System.Windows;
using Eniux.Windows.ThreeDEngine.Helper;
using Eniux.Windows.ThreeDEngine.Geometry;

namespace Eniux.Windows.ThreeDEngine.Visual3Ds
{
    /// <summary>
    ///移动操纵器
    /// </summary>
    public class TranslateManipulator : Manipulator
    {
        #region 变量

        /// <summary>
        ///   最后的点
        /// </summary>
        private Point3D lastPoint;

        #endregion

        #region 注册属性

        #region Diameter

        /// <summary>
        ///   直径属性
        /// </summary>
        public static readonly DependencyProperty DiameterProperty = DependencyProperty.Register(
            "Diameter", typeof(double), typeof(TranslateManipulator), new UIPropertyMetadata(0.2, GeometryChanged));

        /// <summary>
        /// 获取或设置直径
        /// </summary>
        public double Diameter
        {
            get { return (double)GetValue(DiameterProperty); }
            set { SetValue(DiameterProperty, value); }
        } 
        #endregion

        #region Direction

        /// <summary>
        ///  方向属性
        /// </summary>
        public static readonly DependencyProperty DirectionProperty = DependencyProperty.Register(
            "Direction",
            typeof(Vector3D),
            typeof(TranslateManipulator),
            new UIPropertyMetadata(new Vector3D(0, 0, 1), GeometryChanged));

        /// <summary>
        /// 获取或设置方向
        /// </summary>
        public Vector3D Direction
        {
            get { return (Vector3D)GetValue(DirectionProperty); }
            set { SetValue(DirectionProperty, value); }
        } 
        #endregion

        #region Length

        /// <summary>
        ///  长度属性
        /// </summary>
        public static readonly DependencyProperty LengthProperty = DependencyProperty.Register(
            "Length", typeof(double), typeof(TranslateManipulator), new UIPropertyMetadata(2.0, GeometryChanged));

        /// <summary>
        /// 获取或设置长度
        /// </summary>
        public double Length
        {
            get { return (double)GetValue(LengthProperty); }
            set { SetValue(LengthProperty, value); }
        } 
        #endregion

        #endregion

        #region 方法

        /// <summary>
        ///重写几何模型改变事件
        /// </summary>
        protected override void OnGeometryChanged()
        {
            var mb = new MeshBuilder();
            var p0 = new Point3D(0, 0, 0);
            Vector3D d = this.Direction;
            d.Normalize();
            Point3D p1 = p0 + d * this.Length;
            mb.AddArrow(p0, p1, this.Diameter);
            this.model.Geometry = mb.ToMesh();
        }

        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            base.OnMouseDown(e);
            Vector3D wDirection = this.ToWorld(this.Direction);

            Vector3D up = Vector3D.CrossProduct(this.Camera.LookDirection, wDirection);
            Point3D HitPlaneOrigin = this.ToWorld(this.Position);
            this.HitPlaneNormal = Vector3D.CrossProduct(up, wDirection);
            Point p = e.GetPosition(this.ParentViewport);

            Point3D? np = this.GetNearestPoint(p, HitPlaneOrigin, this.HitPlaneNormal);
            if (np == null)
            {
                return;
            }

            Point3D lp = this.ToLocal(np.Value);

            this.lastPoint = lp;
            this.CaptureMouse();
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (this.IsMouseCaptured)
            {
                Point3D HitPlaneOrigin = this.ToWorld(this.Position);
                Point p = e.GetPosition(this.ParentViewport);
                Point3D? nearestPoint = this.GetNearestPoint(p, HitPlaneOrigin, this.HitPlaneNormal);
                if (nearestPoint == null)
                {
                    return;
                }

                Vector3D delta = this.ToLocal(nearestPoint.Value) - this.lastPoint;
                this.Value += Vector3D.DotProduct(delta, this.Direction);

                if (this.TargetTransform != null)
                {
                    var translateTransform = new TranslateTransform3D(delta);
                    this.TargetTransform = Transform3DHelper.CombineTransform(translateTransform, this.TargetTransform);

                    // this.Transform = Transform3DHelper.CombineTransform(translateTransform, this.Transform);
                }
                else
                {
                    this.Position += delta;
                }

                nearestPoint = this.GetNearestPoint(p, HitPlaneOrigin, this.HitPlaneNormal);
                if (nearestPoint != null)
                {
                    this.lastPoint = this.ToLocal(nearestPoint.Value);
                }
            }
        }

        private Point3D? GetNearestPoint(Point position, Point3D hitPlaneOrigin, Vector3D hitPlaneNormal)
        {
            Point3D? hpp = this.GetHitPlanePoint(position, hitPlaneOrigin, hitPlaneNormal);
            if (hpp == null)
            {
                return null;
            }

            var ray = new Ray3D(this.ToWorld(this.Position), this.ToWorld(this.Direction));
            return ray.GetNearest(hpp.Value);
        }

        #endregion
    }
}
